/*
 * $Id: PacketSocket.cc,v 1.7 2005-12-24 18:08:56 bacon Exp $
 */

#include <xpp/net/PacketSocket.h>
#include <xp/bas/string.h>
#include <xp/bas/memory.h>
#include <xp/bas/locale.h>

#ifndef _WIN32
	#include <sys/ioctl.h>
	#include <net/if.h>
#endif

namespace xpp
{
	namespace net
	{

		PacketSocket::PacketSocket (int type): Socket (type)
		{
		}

		int PacketSocket::open ()
		{
			XPP_NET_SOCKET_CHECK_OPEN_HANDLE (this->sckhnd);
		#ifndef PF_PACKET
			return -1;
		#else
			if (create_socket (PF_PACKET, socket_type, 0) == -1) return -1;
			return 0;
		#endif
		}

		int PacketSocket::getIfceIndex (const xp_char_t* name)
		{
		#ifndef PF_PACKET
			return -1;
		#else
			struct ifreq ifr;
		
			xp_memset (&ifr, 0, xp_sizeof(ifr));		
		#ifdef XP_CHAR_IS_MCHAR
			xp_strxcpy (ifr.ifr_name, xp_countof(ifr.ifr_name), name);
		#else
			xp_wcstomcs (name, ifr.ifr_name, xp_countof(ifr.ifr_name));
		#endif
			if (ioctl(this->sckhnd, SIOCGIFINDEX, &ifr) == -1) return -1;

			return ifr.ifr_ifindex;
		#endif
		}

		int PacketSocket::getIfceName (int index, xp_char_t* buf, xp_size_t size)
		{
		#ifndef PF_PACKET
			return -1;
		#else
			struct ifreq ifr;
			
			xp_memset (&ifr, 0, xp_sizeof(ifr));
			ifr.ifr_ifindex = index;

			if (ioctl(this->sckhnd, SIOCGIFNAME, &ifr) == -1) return -1;
		#ifdef XP_CHAR_IS_MCHAR
			xp_strxcpy (buf, size, ifr.ifr_name);
		#else
			xp_mcstowcs (ifr.ifr_name, buf, size);
		#endif
			return 0;
		#endif
		}

		int PacketSocket::getIfceEtherAddress (
			const xp_char_t* name/*, EtherAddress* addr*/)
		{
		#ifndef PF_PACKET
			return -1;
		#else
			struct ifreq ifr;
		
			xp_memset (&ifr, 0, xp_sizeof(ifr));		
		#ifdef XP_CHAR_IS_MCHAR
			xp_strxcpy (ifr.ifr_name, xp_countof(ifr.ifr_name), name);
		#else
			xp_wcstomcs (name, ifr.ifr_name, xp_countof(ifr.ifr_name));
		#endif
			if (ioctl(this->sckhnd, SIOCGIFHWADDR, &ifr) == -1) return -1;
			
			struct sockaddr* sa = (struct sockaddr*)&ifr.ifr_addr;
			xp_printf (XP_TEXT("ether family: %d\n"), sa->sa_family);

			return 0;
		#endif
		}

		int PacketSocket::getIfceInetAddress (
			const xp_char_t* name, InetAddress* addr)
		{
		#ifdef SIOCGIFADDR
			return get_ifce_address (name, SIOCGIFADDR, addr);
		#else
			return -1;
		#endif
		}

		int PacketSocket::getIfceNetmask (
			const xp_char_t* name, InetAddress* addr)
		{
		#ifdef SIOCGIFNETMASK
			return get_ifce_address (name, SIOCGIFNETMASK, addr);
		#else
			return 1;
		#endif
		}

		int PacketSocket::getIfceBroadcast (
			const xp_char_t* name, InetAddress* addr)
		{
		#ifdef SIOCGIFBRDADDR
			return get_ifce_address (name, SIOCGIFBRDADDR, addr);
		#else
			return -1;
		#endif
		}

		int PacketSocket::get_ifce_address (
			const xp_char_t* name, int cmd, InetAddress* addr)
		{
		#ifndef PF_PACKET
			return -1;
		#else
			struct ifreq ifr;

			xp_memset (&ifr, 0, xp_sizeof(ifr));		
		#ifdef XP_CHAR_IS_MCHAR
			xp_strxcpy (ifr.ifr_name, xp_countof(ifr.ifr_name), name);
		#else
			xp_wcstomcs (name, ifr.ifr_name, xp_countof(ifr.ifr_name));
		#endif

			if (ioctl (this->sckhnd, cmd, &ifr) == -1) return -1;

			struct sockaddr* sa = (struct sockaddr*)&ifr.ifr_addr;
			if (sa->sa_family == InetAddress::V4) {
				*addr = InetAddress (*(struct sockaddr_in*)sa);
			}
			else if (sa->sa_family == InetAddress::V6) {
				*addr = InetAddress (*(struct sockaddr_in6*)sa);
			}
			else return -1;

			return 0;	
		#endif
		}

	}
}
